package com.acoreful.acf2java.admin.modules.auth.support.web.interceptor;

import java.util.Arrays;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.acoreful.acf2java.admin.config.AdminConfig;
import com.acoreful.acf2java.admin.modules.auth.exception.LoginRequiredException;
import com.acoreful.acf2java.admin.modules.auth.exception.NoAuthorityException;
import com.acoreful.acf2java.admin.modules.auth.service.AuthService;
import com.acoreful.acf2java.admin.modules.auth.support.CurrentStaff;
import com.acoreful.acf2java.admin.modules.auth.support.SubjectContext;
import com.acoreful.acf2java.admin.modules.auth.support.annotation.Logical;
import com.acoreful.acf2java.admin.modules.auth.support.annotation.RequiresPermissions;
import com.acoreful.acf2java.admin.modules.auth.support.annotation.RequiresRoles;
import com.acoreful.acf2java.admin.modules.auth.support.annotation.Subject;
import com.acoreful.acf2java.commons.utils.HttpServletUtils;

/**
 * 权限认证拦截器
 * 
 */
public class AuthenticationInterceptor extends HandlerInterceptorAdapter {
    private static Logger logger=LoggerFactory.getLogger(AuthenticationInterceptor.class);

    @Autowired
    private AuthService authService;
    
    @Autowired
    private AdminConfig config;
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            boolean loginRequired = isLoginRequired(handlerMethod); // 是否要求登录后才能访问
            logger.info("URI:{}",request.getRequestURI());
            CurrentStaff currentStaff = getCurrentStaff(request);
            if (loginRequired && currentStaff == null) { // 方法要求登录, 但又获取不到当前登录用户
                response.sendRedirect(config.getAppUrlLogin()); // 重定向到登录页
                return false;
            }
            
            if (currentStaff != null) {
            	authService.postAuthentication(currentStaff);
                checkRequiresRolesIfNesssary(handlerMethod, currentStaff);
                checkRequiresPermissionsIfNesssary(handlerMethod, currentStaff);
            }
            
            SubjectContext.getContext().setCurrentStaff(currentStaff);
        }
        return super.preHandle(request, response, handler);
    }

	private boolean isLoginRequired(HandlerMethod handlerMethod) {
		RequiresRoles annoRequiresRoles = handlerMethod.getMethodAnnotation(RequiresRoles.class);
		RequiresPermissions annoRequiresPermissons = handlerMethod.getMethodAnnotation(RequiresPermissions.class);
		return annoRequiresRoles != null || annoRequiresPermissons != null
				|| hasSubjectAnnotation(handlerMethod);
	}

    private boolean hasSubjectAnnotation(HandlerMethod handlerMethod) {
        for (MethodParameter param : handlerMethod.getMethodParameters()) {
            Subject annoSubject = param.getParameterAnnotation(Subject.class);
            if (annoSubject != null && annoSubject.required()) { // @Subject 或 @Subject(required = true)
                return true;
            }
        }
        return false;
    }

    private CurrentStaff getCurrentStaff(HttpServletRequest request) {
    	String accessToken = HttpServletUtils.getCookie(request, AuthService.ACCESS_TOKEN);
    	return authService.getCurrentStaff(accessToken);
    }
    
    private void checkRequiresRolesIfNesssary(HandlerMethod handlerMethod, CurrentStaff currentStaff) {
        RequiresRoles annoRequiresRoles = handlerMethod.getMethodAnnotation(RequiresRoles.class);
        if (annoRequiresRoles != null) {
            List<String> sourceRoles = Arrays.asList(annoRequiresRoles.value());
            if (CollectionUtils.isNotEmpty(sourceRoles) && currentStaff == null) {
                throw new LoginRequiredException();
            }
            
            List<String> roles = currentStaff.getRoles();
            
            Logical logical = annoRequiresRoles.logical();
            switch (logical) {
                case AND:
                    if (!requireContains(sourceRoles, roles)) {
                        throw new NoAuthorityException("您没有被授权此角色");
                    }
                    break;
                case OR:
                    if (!mayContains(sourceRoles, roles)) {
                        throw new NoAuthorityException("您没有被授权此角色");
                    }
                    break;
                default:
                    throw new IllegalStateException("错误的Logical - " + logical);
            }
        }
    }
    
    private void checkRequiresPermissionsIfNesssary(HandlerMethod handlerMethod, CurrentStaff currentStaff) {
        RequiresPermissions annoRequiresPermissons = handlerMethod.getMethodAnnotation(RequiresPermissions.class);
        if (annoRequiresPermissons != null) {
            List<String> sourcePermissions = Arrays.asList(annoRequiresPermissons.value());
            if (CollectionUtils.isNotEmpty(sourcePermissions) && currentStaff == null) {
                throw new LoginRequiredException();
            }
            
            List<String> permissions = currentStaff.getPermissions();
            
            Logical logical = annoRequiresPermissons.logical();
            switch (logical) {
                case AND:
                    if (!requireContains(sourcePermissions, permissions)) {
                        throw new NoAuthorityException("您没有被授权此权限");
                    }
                    break;
                case OR:
                    if (!mayContains(sourcePermissions, permissions)) {
                        throw new NoAuthorityException("您没有被授权此权限");
                    }
                    break;
                default:
                    throw new IllegalStateException("错误的Logical - " + logical);
            }
        }
    }
    
    private boolean requireContains(List<String> source, List<String> target) {
        return CollectionUtils.isSubCollection(source, target);
    }
    
    private boolean mayContains(List<String> source, List<String> target) {
        return CollectionUtils.intersection(source, target).size() > 0;
    }
    
}
